package cn.source.system.dingding.service;

import cn.source.system.common.Util.cache.NewRedisCache;
import cn.source.system.common.constant.CommonConstants;
import cn.source.system.dingding.config.DingAppConfig;
import cn.source.system.dingding.config.DingUrlConstant;
import cn.source.system.dingding.domain.ServiceResult;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.request.OapiGetJsapiTicketRequest;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.response.OapiGetJsapiTicketResponse;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.taobao.api.ApiException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class DingDingTokenService {
    @Resource
    private NewRedisCache newRedisCache;

    /**
     * 缓存时间：一小时50分钟
     */
    private static final long CACHE_TTL = 60 * 55 * 2;

    @Resource
    private DingAppConfig dingAppConfig;

    /**
     * 在此方法中，为了避免频繁获取access_token，
     * 在距离上一次获取access_token时间在两个小时之内的情况，
     * 将直接从持久化存储中读取access_token
     * <p>
     * 因为access_token和jsapi_ticket的过期时间都是7200秒
     * 所以在获取access_token的同时也去获取了jsapi_ticket
     * 注：jsapi_ticket是在前端页面JSAPI做权限验证配置的时候需要使用的
     * 具体信息请查看开发者文档--权限验证配置
     *
     * @return accessToken 或错误信息
     */
    public ServiceResult<String> getAccessToken() {
        // 从持久化存储中读取
        String accessToken = newRedisCache.get(CommonConstants.DAILY_DING_ACCESS_TOKEN);
        log.info("从Redis缓存中获取到的accessToken = {}", accessToken);
        if (accessToken != null) {
            return ServiceResult.success(accessToken);
        }

        DefaultDingTalkClient client = new DefaultDingTalkClient(DingUrlConstant.URL_GET_TOKEN);
        OapiGettokenRequest request = new OapiGettokenRequest();
        OapiGettokenResponse response;

        request.setAppkey(dingAppConfig.getAppKey());
        request.setAppsecret(dingAppConfig.getAppSecret());
        request.setHttpMethod("GET");

        try {
            response = client.execute(request);
        } catch (ApiException e) {
            log.error("getAccessToken failed", e);
            return ServiceResult.failure(e.getErrCode(), e.getErrMsg());
        }

        accessToken = response.getAccessToken();
        log.info("向Redis缓存中存取accessToken = {}", accessToken);
        newRedisCache.set(CommonConstants.DAILY_DING_ACCESS_TOKEN, accessToken, CACHE_TTL, TimeUnit.SECONDS);

        return ServiceResult.success(accessToken);
    }

    /**
     * 获取JSTicket, 用于js的签名计算
     * 正常的情况下，jsapi_ticket的有效期为7200秒，所以开发者需要在某个地方设计一个定时器，定期去更新jsapi_ticket
     *
     * @return jsTicket或错误信息
     */
    public ServiceResult<String> getJsTicket() {
        // 从持久化存储中读取
        String ticket = newRedisCache.get(CommonConstants.DAILY_DING_JS_TICKET);
        if (ticket != null) {
            return ServiceResult.success(ticket);
        }

        String accessToken;
        ServiceResult<String> tokenSr = getAccessToken();
        if (!tokenSr.isSuccess()) {
            return ServiceResult.failure(tokenSr.getCode(), tokenSr.getMessage());
        }
        accessToken = tokenSr.getResult();

        DefaultDingTalkClient client = new DefaultDingTalkClient(DingUrlConstant.URL_GET_JSTICKET);
        OapiGetJsapiTicketRequest request = new OapiGetJsapiTicketRequest();
        OapiGetJsapiTicketResponse response;

        request.setHttpMethod("GET");

        try {
            response = client.execute(request, accessToken);
        } catch (ApiException e) {
            log.error("getAccessToken failed", e);
            return ServiceResult.failure(e.getErrCode(), e.getErrMsg());
        }

        if (!response.isSuccess()) {
            return ServiceResult.failure(response.getErrorCode(), response.getErrmsg());
        }
        ticket = response.getTicket();
        newRedisCache.set(CommonConstants.DAILY_DING_JS_TICKET, ticket, CACHE_TTL, TimeUnit.SECONDS);

        return ServiceResult.success(ticket);
    }
}
